home *** CD-ROM | disk | FTP | other *** search
/ American Osteopathic Ass…tion Yearbook 2005 & 2006 / American Osteopathic Association Yearbook 2005 & 2006.iso / mac / app / urllib.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2004-07-22  |  58.7 KB  |  1,700 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.3)
  3.  
  4. '''Open an arbitrary URL.
  5.  
  6. See the following document for more info on URLs:
  7. "Names and Addresses, URIs, URLs, URNs, URCs", at
  8. http://www.w3.org/pub/WWW/Addressing/Overview.html
  9.  
  10. See also the HTTP spec (from which the error codes are derived):
  11. "HTTP - Hypertext Transfer Protocol", at
  12. http://www.w3.org/pub/WWW/Protocols/
  13.  
  14. Related standards and specs:
  15. - RFC1808: the "relative URL" spec. (authoritative status)
  16. - RFC1738 - the "URL standard". (authoritative status)
  17. - RFC1630 - the "URI spec". (informational status)
  18.  
  19. The object returned by URLopener().open(file) will differ per
  20. protocol.  All you know is that is has methods read(), readline(),
  21. readlines(), fileno(), close() and info().  The read*(), fileno()
  22. and close() methods work like those of open files.
  23. The info() method returns a mimetools.Message object which can be
  24. used to query various info about the object, if available.
  25. (mimetools.Message objects are queried with the getheader() method.)
  26. '''
  27. import string
  28. import socket
  29. import os
  30. import time
  31. import sys
  32. __all__ = [
  33.     'urlopen',
  34.     'URLopener',
  35.     'FancyURLopener',
  36.     'urlretrieve',
  37.     'urlcleanup',
  38.     'quote',
  39.     'quote_plus',
  40.     'unquote',
  41.     'unquote_plus',
  42.     'urlencode',
  43.     'url2pathname',
  44.     'pathname2url',
  45.     'splittag',
  46.     'localhost',
  47.     'thishost',
  48.     'ftperrors',
  49.     'basejoin',
  50.     'unwrap',
  51.     'splittype',
  52.     'splithost',
  53.     'splituser',
  54.     'splitpasswd',
  55.     'splitport',
  56.     'splitnport',
  57.     'splitquery',
  58.     'splitattr',
  59.     'splitvalue',
  60.     'splitgophertype',
  61.     'getproxies']
  62. __version__ = '1.15'
  63. MAXFTPCACHE = 10
  64. if os.name == 'mac':
  65.     from macurl2path import url2pathname, pathname2url
  66. elif os.name == 'nt':
  67.     from nturl2path import url2pathname, pathname2url
  68. elif os.name == 'riscos':
  69.     from rourl2path import url2pathname, pathname2url
  70. else:
  71.     
  72.     def url2pathname(pathname):
  73.         return unquote(pathname)
  74.  
  75.     
  76.     def pathname2url(pathname):
  77.         return quote(pathname)
  78.  
  79. _urlopener = None
  80.  
  81. def urlopen(url, data = None, proxies = None):
  82.     '''urlopen(url [, data]) -> open file-like object'''
  83.     global _urlopener
  84.     if proxies is not None:
  85.         opener = FancyURLopener(proxies = proxies)
  86.     elif not _urlopener:
  87.         opener = FancyURLopener()
  88.         _urlopener = opener
  89.     else:
  90.         opener = _urlopener
  91.     if data is None:
  92.         return opener.open(url)
  93.     else:
  94.         return opener.open(url, data)
  95.  
  96.  
  97. def urlretrieve(url, filename = None, reporthook = None, data = None):
  98.     global _urlopener
  99.     if not _urlopener:
  100.         _urlopener = FancyURLopener()
  101.     
  102.     return _urlopener.retrieve(url, filename, reporthook, data)
  103.  
  104.  
  105. def urlcleanup():
  106.     if _urlopener:
  107.         _urlopener.cleanup()
  108.     
  109.  
  110. ftpcache = { }
  111.  
  112. class URLopener:
  113.     """Class to open URLs.
  114.     This is a class rather than just a subroutine because we may need
  115.     more than one set of global protocol-specific options.
  116.     Note -- this is a base class for those who don't want the
  117.     automatic handling of errors type 302 (relocated) and 401
  118.     (authorization needed)."""
  119.     __tempfiles = None
  120.     version = 'Python-urllib/%s' % __version__
  121.     
  122.     def __init__(self, proxies = None, **x509):
  123.         if proxies is None:
  124.             proxies = getproxies()
  125.         
  126.         if not hasattr(proxies, 'has_key'):
  127.             raise AssertionError, 'proxies must be a mapping'
  128.         self.proxies = proxies
  129.         self.key_file = x509.get('key_file')
  130.         self.cert_file = x509.get('cert_file')
  131.         self.addheaders = [
  132.             ('User-agent', self.version)]
  133.         self._URLopener__tempfiles = []
  134.         self._URLopener__unlink = os.unlink
  135.         self.tempcache = None
  136.         self.ftpcache = ftpcache
  137.  
  138.     
  139.     def __del__(self):
  140.         self.close()
  141.  
  142.     
  143.     def close(self):
  144.         self.cleanup()
  145.  
  146.     
  147.     def cleanup(self):
  148.         if self._URLopener__tempfiles:
  149.             for file in self._URLopener__tempfiles:
  150.                 
  151.                 try:
  152.                     self._URLopener__unlink(file)
  153.                 continue
  154.                 except OSError:
  155.                     continue
  156.                 
  157.  
  158.             
  159.             del self._URLopener__tempfiles[:]
  160.         
  161.         if self.tempcache:
  162.             self.tempcache.clear()
  163.         
  164.  
  165.     
  166.     def addheader(self, *args):
  167.         """Add a header to be used by the HTTP interface only
  168.         e.g. u.addheader('Accept', 'sound/basic')"""
  169.         self.addheaders.append(args)
  170.  
  171.     
  172.     def open(self, fullurl, data = None):
  173.         """Use URLopener().open(file) instead of open(file, 'r')."""
  174.         fullurl = unwrap(toBytes(fullurl))
  175.         if self.tempcache and fullurl in self.tempcache:
  176.             (filename, headers) = self.tempcache[fullurl]
  177.             fp = open(filename, 'rb')
  178.             return addinfourl(fp, headers, fullurl)
  179.         
  180.         (urltype, url) = splittype(fullurl)
  181.         if not urltype:
  182.             urltype = 'file'
  183.         
  184.         if urltype in self.proxies:
  185.             proxy = self.proxies[urltype]
  186.             (urltype, proxyhost) = splittype(proxy)
  187.             (host, selector) = splithost(proxyhost)
  188.             url = (host, fullurl)
  189.         else:
  190.             proxy = None
  191.         name = 'open_' + urltype
  192.         self.type = urltype
  193.         if '-' in name:
  194.             name = '_'.join(name.split('-'))
  195.         
  196.         if not hasattr(self, name):
  197.             if proxy:
  198.                 return self.open_unknown_proxy(proxy, fullurl, data)
  199.             else:
  200.                 return self.open_unknown(fullurl, data)
  201.         
  202.         
  203.         try:
  204.             if data is None:
  205.                 return getattr(self, name)(url)
  206.             else:
  207.                 return getattr(self, name)(url, data)
  208.         except socket.error:
  209.             msg = None
  210.             raise IOError, ('socket error', msg), sys.exc_info()[2]
  211.  
  212.  
  213.     
  214.     def open_unknown(self, fullurl, data = None):
  215.         '''Overridable interface to open unknown URL type.'''
  216.         (type, url) = splittype(fullurl)
  217.         raise IOError, ('url error', 'unknown url type', type)
  218.  
  219.     
  220.     def open_unknown_proxy(self, proxy, fullurl, data = None):
  221.         '''Overridable interface to open unknown URL type.'''
  222.         (type, url) = splittype(fullurl)
  223.         raise IOError, ('url error', 'invalid proxy for %s' % type, proxy)
  224.  
  225.     
  226.     def retrieve(self, url, filename = None, reporthook = None, data = None):
  227.         '''retrieve(url) returns (filename, headers) for a local object
  228.         or (tempfilename, headers) for a remote object.'''
  229.         url = unwrap(toBytes(url))
  230.         if self.tempcache and url in self.tempcache:
  231.             return self.tempcache[url]
  232.         
  233.         (type, url1) = splittype(url)
  234.         if filename is None and not type or type == 'file':
  235.             
  236.             try:
  237.                 fp = self.open_local_file(url1)
  238.                 hdrs = fp.info()
  239.                 del fp
  240.                 return (url2pathname(splithost(url1)[1]), hdrs)
  241.             except IOError:
  242.                 msg = None
  243.             except:
  244.                 None<EXCEPTION MATCH>IOError
  245.             
  246.  
  247.         None<EXCEPTION MATCH>IOError
  248.         fp = self.open(url, data)
  249.         headers = fp.info()
  250.         if filename:
  251.             tfp = open(filename, 'wb')
  252.         else:
  253.             import tempfile
  254.             (garbage, path) = splittype(url)
  255.             if not path:
  256.                 pass
  257.             (garbage, path) = splithost('')
  258.             if not path:
  259.                 pass
  260.             (path, garbage) = splitquery('')
  261.             if not path:
  262.                 pass
  263.             (path, garbage) = splitattr('')
  264.             suffix = os.path.splitext(path)[1]
  265.             (fd, filename) = tempfile.mkstemp(suffix)
  266.             self._URLopener__tempfiles.append(filename)
  267.             tfp = os.fdopen(fd, 'wb')
  268.         result = (filename, headers)
  269.         if self.tempcache is not None:
  270.             self.tempcache[url] = result
  271.         
  272.         bs = 1024 * 8
  273.         size = -1
  274.         blocknum = 1
  275.         if reporthook:
  276.             if 'content-length' in headers:
  277.                 size = int(headers['Content-Length'])
  278.             
  279.             reporthook(0, bs, size)
  280.         
  281.         block = fp.read(bs)
  282.         if reporthook:
  283.             reporthook(1, bs, size)
  284.         
  285.         while block:
  286.             tfp.write(block)
  287.             block = fp.read(bs)
  288.             blocknum = blocknum + 1
  289.             if reporthook:
  290.                 reporthook(blocknum, bs, size)
  291.                 continue
  292.         fp.close()
  293.         tfp.close()
  294.         del fp
  295.         del tfp
  296.         return result
  297.  
  298.     
  299.     def open_http(self, url, data = None):
  300.         '''Use HTTP protocol.'''
  301.         import httplib
  302.         user_passwd = None
  303.         if isinstance(url, str):
  304.             (host, selector) = splithost(url)
  305.             if host:
  306.                 (user_passwd, host) = splituser(host)
  307.                 host = unquote(host)
  308.             
  309.             realhost = host
  310.         else:
  311.             (host, selector) = url
  312.             (urltype, rest) = splittype(selector)
  313.             url = rest
  314.             user_passwd = None
  315.             if urltype.lower() != 'http':
  316.                 realhost = None
  317.             else:
  318.                 (realhost, rest) = splithost(rest)
  319.                 if realhost:
  320.                     (user_passwd, realhost) = splituser(realhost)
  321.                 
  322.                 if user_passwd:
  323.                     selector = '%s://%s%s' % (urltype, realhost, rest)
  324.                 
  325.                 if proxy_bypass(realhost):
  326.                     host = realhost
  327.                 
  328.         if not host:
  329.             raise IOError, ('http error', 'no host given')
  330.         
  331.         if user_passwd:
  332.             import base64
  333.             auth = base64.encodestring(user_passwd).strip()
  334.         else:
  335.             auth = None
  336.         h = httplib.HTTP(host)
  337.         if data is not None:
  338.             h.putrequest('POST', selector)
  339.             h.putheader('Content-type', 'application/x-www-form-urlencoded')
  340.             h.putheader('Content-length', '%d' % len(data))
  341.         else:
  342.             h.putrequest('GET', selector)
  343.         if auth:
  344.             h.putheader('Authorization', 'Basic %s' % auth)
  345.         
  346.         if realhost:
  347.             h.putheader('Host', realhost)
  348.         
  349.         for args in self.addheaders:
  350.             h.putheader(*args)
  351.         
  352.         h.endheaders()
  353.         if data is not None:
  354.             h.send(data)
  355.         
  356.         (errcode, errmsg, headers) = h.getreply()
  357.         fp = h.getfile()
  358.         if errcode == 200:
  359.             return addinfourl(fp, headers, 'http:' + url)
  360.         elif data is None:
  361.             return self.http_error(url, fp, errcode, errmsg, headers)
  362.         else:
  363.             return self.http_error(url, fp, errcode, errmsg, headers, data)
  364.  
  365.     
  366.     def http_error(self, url, fp, errcode, errmsg, headers, data = None):
  367.         '''Handle http errors.
  368.         Derived class can override this, or provide specific handlers
  369.         named http_error_DDD where DDD is the 3-digit error code.'''
  370.         name = 'http_error_%d' % errcode
  371.         if hasattr(self, name):
  372.             method = getattr(self, name)
  373.             if data is None:
  374.                 result = method(url, fp, errcode, errmsg, headers)
  375.             else:
  376.                 result = method(url, fp, errcode, errmsg, headers, data)
  377.             if result:
  378.                 return result
  379.             
  380.         
  381.         return self.http_error_default(url, fp, errcode, errmsg, headers)
  382.  
  383.     
  384.     def http_error_default(self, url, fp, errcode, errmsg, headers):
  385.         '''Default error handler: close the connection and raise IOError.'''
  386.         void = fp.read()
  387.         fp.close()
  388.         raise IOError, ('http error', errcode, errmsg, headers)
  389.  
  390.     if hasattr(socket, 'ssl'):
  391.         
  392.         def open_https(self, url, data = None):
  393.             '''Use HTTPS protocol.'''
  394.             import httplib
  395.             user_passwd = None
  396.             if isinstance(url, str):
  397.                 (host, selector) = splithost(url)
  398.                 if host:
  399.                     (user_passwd, host) = splituser(host)
  400.                     host = unquote(host)
  401.                 
  402.                 realhost = host
  403.             else:
  404.                 (host, selector) = url
  405.                 (urltype, rest) = splittype(selector)
  406.                 url = rest
  407.                 user_passwd = None
  408.                 if urltype.lower() != 'https':
  409.                     realhost = None
  410.                 else:
  411.                     (realhost, rest) = splithost(rest)
  412.                     if realhost:
  413.                         (user_passwd, realhost) = splituser(realhost)
  414.                     
  415.                     if user_passwd:
  416.                         selector = '%s://%s%s' % (urltype, realhost, rest)
  417.                     
  418.             if not host:
  419.                 raise IOError, ('https error', 'no host given')
  420.             
  421.             if user_passwd:
  422.                 import base64
  423.                 auth = base64.encodestring(user_passwd).strip()
  424.             else:
  425.                 auth = None
  426.             h = httplib.HTTPS(host, 0, key_file = self.key_file, cert_file = self.cert_file)
  427.             if data is not None:
  428.                 h.putrequest('POST', selector)
  429.                 h.putheader('Content-type', 'application/x-www-form-urlencoded')
  430.                 h.putheader('Content-length', '%d' % len(data))
  431.             else:
  432.                 h.putrequest('GET', selector)
  433.             if auth:
  434.                 h.putheader('Authorization: Basic %s' % auth)
  435.             
  436.             if realhost:
  437.                 h.putheader('Host', realhost)
  438.             
  439.             for args in self.addheaders:
  440.                 h.putheader(*args)
  441.             
  442.             h.endheaders()
  443.             if data is not None:
  444.                 h.send(data)
  445.             
  446.             (errcode, errmsg, headers) = h.getreply()
  447.             fp = h.getfile()
  448.             if errcode == 200:
  449.                 return addinfourl(fp, headers, 'https:' + url)
  450.             elif data is None:
  451.                 return self.http_error(url, fp, errcode, errmsg, headers)
  452.             else:
  453.                 return self.http_error(url, fp, errcode, errmsg, headers, data)
  454.  
  455.     
  456.     
  457.     def open_gopher(self, url):
  458.         '''Use Gopher protocol.'''
  459.         import gopherlib
  460.         (host, selector) = splithost(url)
  461.         if not host:
  462.             raise IOError, ('gopher error', 'no host given')
  463.         
  464.         host = unquote(host)
  465.         (type, selector) = splitgophertype(selector)
  466.         (selector, query) = splitquery(selector)
  467.         selector = unquote(selector)
  468.         if query:
  469.             query = unquote(query)
  470.             fp = gopherlib.send_query(selector, query, host)
  471.         else:
  472.             fp = gopherlib.send_selector(selector, host)
  473.         return addinfourl(fp, noheaders(), 'gopher:' + url)
  474.  
  475.     
  476.     def open_file(self, url):
  477.         '''Use local file or FTP depending on form of URL.'''
  478.         if url[:2] == '//' and url[2:3] != '/' and url[2:12].lower() != 'localhost/':
  479.             return self.open_ftp(url)
  480.         else:
  481.             return self.open_local_file(url)
  482.  
  483.     
  484.     def open_local_file(self, url):
  485.         '''Use local file.'''
  486.         import mimetypes
  487.         import mimetools
  488.         import rfc822
  489.         import StringIO
  490.         (host, file) = splithost(url)
  491.         localname = url2pathname(file)
  492.         
  493.         try:
  494.             stats = os.stat(localname)
  495.         except OSError:
  496.             e = None
  497.             raise IOError(e.errno, e.strerror, e.filename)
  498.  
  499.         size = stats.st_size
  500.         modified = rfc822.formatdate(stats.st_mtime)
  501.         mtype = mimetypes.guess_type(url)[0]
  502.         if not mtype:
  503.             pass
  504.         headers = mimetools.Message(StringIO.StringIO('Content-Type: %s\nContent-Length: %d\nLast-modified: %s\n' % ('text/plain', size, modified)))
  505.         if not host:
  506.             urlfile = file
  507.             if file[:1] == '/':
  508.                 urlfile = 'file://' + file
  509.             
  510.             return addinfourl(open(localname, 'rb'), headers, urlfile)
  511.         
  512.         (host, port) = splitport(host)
  513.         if not port and socket.gethostbyname(host) in (localhost(), thishost()):
  514.             urlfile = file
  515.             if file[:1] == '/':
  516.                 urlfile = 'file://' + file
  517.             
  518.             return addinfourl(open(localname, 'rb'), headers, urlfile)
  519.         
  520.         raise IOError, ('local file error', 'not on local host')
  521.  
  522.     
  523.     def open_ftp(self, url):
  524.         '''Use FTP protocol.'''
  525.         import mimetypes
  526.         import mimetools
  527.         import StringIO
  528.         (host, path) = splithost(url)
  529.         if not host:
  530.             raise IOError, ('ftp error', 'no host given')
  531.         
  532.         (host, port) = splitport(host)
  533.         (user, host) = splituser(host)
  534.         if user:
  535.             (user, passwd) = splitpasswd(user)
  536.         else:
  537.             passwd = None
  538.         host = unquote(host)
  539.         if not user:
  540.             pass
  541.         user = unquote('')
  542.         if not passwd:
  543.             pass
  544.         passwd = unquote('')
  545.         host = socket.gethostbyname(host)
  546.         if not port:
  547.             import ftplib
  548.             port = ftplib.FTP_PORT
  549.         else:
  550.             port = int(port)
  551.         (path, attrs) = splitattr(path)
  552.         path = unquote(path)
  553.         dirs = path.split('/')
  554.         (dirs, file) = (dirs[:-1], dirs[-1])
  555.         if dirs and not dirs[0]:
  556.             dirs = dirs[1:]
  557.         
  558.         if dirs and not dirs[0]:
  559.             dirs[0] = '/'
  560.         
  561.         key = (user, host, port, '/'.join(dirs))
  562.         if len(self.ftpcache) > MAXFTPCACHE:
  563.             for k in self.ftpcache.keys():
  564.                 if k != key:
  565.                     v = self.ftpcache[k]
  566.                     del self.ftpcache[k]
  567.                     v.close()
  568.                     continue
  569.             
  570.         
  571.         
  572.         try:
  573.             if not (key in self.ftpcache):
  574.                 self.ftpcache[key] = ftpwrapper(user, passwd, host, port, dirs)
  575.             
  576.             if not file:
  577.                 type = 'D'
  578.             else:
  579.                 type = 'I'
  580.             for attr in attrs:
  581.                 (attr, value) = splitvalue(attr)
  582.                 if attr.lower() == 'type' and value in ('a', 'A', 'i', 'I', 'd', 'D'):
  583.                     type = value.upper()
  584.                     continue
  585.             
  586.             (fp, retrlen) = self.ftpcache[key].retrfile(file, type)
  587.             mtype = mimetypes.guess_type('ftp:' + url)[0]
  588.             headers = ''
  589.             if mtype:
  590.                 headers += 'Content-Type: %s\n' % mtype
  591.             
  592.             if retrlen is not None and retrlen >= 0:
  593.                 headers += 'Content-Length: %d\n' % retrlen
  594.             
  595.             headers = mimetools.Message(StringIO.StringIO(headers))
  596.             return addinfourl(fp, headers, 'ftp:' + url)
  597.         except ftperrors():
  598.             msg = None
  599.             raise IOError, ('ftp error', msg), sys.exc_info()[2]
  600.  
  601.  
  602.     
  603.     def open_data(self, url, data = None):
  604.         '''Use "data" URL.'''
  605.         import StringIO
  606.         import mimetools
  607.         
  608.         try:
  609.             (type, data) = url.split(',', 1)
  610.         except ValueError:
  611.             raise IOError, ('data error', 'bad data URL')
  612.  
  613.         if not type:
  614.             type = 'text/plain;charset=US-ASCII'
  615.         
  616.         semi = type.rfind(';')
  617.         if semi >= 0 and '=' not in type[semi:]:
  618.             encoding = type[semi + 1:]
  619.             type = type[:semi]
  620.         else:
  621.             encoding = ''
  622.         msg = []
  623.         msg.append('Date: %s' % time.strftime('%a, %d %b %Y %T GMT', time.gmtime(time.time())))
  624.         msg.append('Content-type: %s' % type)
  625.         if encoding == 'base64':
  626.             import base64
  627.             data = base64.decodestring(data)
  628.         else:
  629.             data = unquote(data)
  630.         msg.append('Content-length: %d' % len(data))
  631.         msg.append('')
  632.         msg.append(data)
  633.         msg = '\n'.join(msg)
  634.         f = StringIO.StringIO(msg)
  635.         headers = mimetools.Message(f, 0)
  636.         f.fileno = None
  637.         return addinfourl(f, headers, url)
  638.  
  639.  
  640.  
  641. class FancyURLopener(URLopener):
  642.     '''Derived class with handlers for errors we can handle (perhaps).'''
  643.     
  644.     def __init__(self, *args, **kwargs):
  645.         URLopener.__init__(self, *args, **kwargs)
  646.         self.auth_cache = { }
  647.         self.tries = 0
  648.         self.maxtries = 10
  649.  
  650.     
  651.     def http_error_default(self, url, fp, errcode, errmsg, headers):
  652.         """Default error handling -- don't raise an exception."""
  653.         return addinfourl(fp, headers, 'http:' + url)
  654.  
  655.     
  656.     def http_error_302(self, url, fp, errcode, errmsg, headers, data = None):
  657.         '''Error 302 -- relocated (temporarily).'''
  658.         self.tries += 1
  659.         result = self.redirect_internal(url, fp, errcode, errmsg, headers, data)
  660.         self.tries = 0
  661.         return result
  662.  
  663.     
  664.     def redirect_internal(self, url, fp, errcode, errmsg, headers, data):
  665.         if 'location' in headers:
  666.             newurl = headers['location']
  667.         elif 'uri' in headers:
  668.             newurl = headers['uri']
  669.         else:
  670.             return None
  671.         void = fp.read()
  672.         fp.close()
  673.         newurl = basejoin(self.type + ':' + url, newurl)
  674.         return self.open(newurl)
  675.  
  676.     
  677.     def http_error_301(self, url, fp, errcode, errmsg, headers, data = None):
  678.         '''Error 301 -- also relocated (permanently).'''
  679.         return self.http_error_302(url, fp, errcode, errmsg, headers, data)
  680.  
  681.     
  682.     def http_error_303(self, url, fp, errcode, errmsg, headers, data = None):
  683.         '''Error 303 -- also relocated (essentially identical to 302).'''
  684.         return self.http_error_302(url, fp, errcode, errmsg, headers, data)
  685.  
  686.     
  687.     def http_error_307(self, url, fp, errcode, errmsg, headers, data = None):
  688.         '''Error 307 -- relocated, but turn POST into error.'''
  689.         if data is None:
  690.             return self.http_error_302(url, fp, errcode, errmsg, headers, data)
  691.         else:
  692.             return self.http_error_default(url, fp, errcode, errmsg, headers)
  693.  
  694.     
  695.     def http_error_401(self, url, fp, errcode, errmsg, headers, data = None):
  696.         '''Error 401 -- authentication required.
  697.         See this URL for a description of the basic authentication scheme:
  698.         http://www.ics.uci.edu/pub/ietf/http/draft-ietf-http-v10-spec-00.txt'''
  699.         if not ('www-authenticate' in headers):
  700.             URLopener.http_error_default(self, url, fp, errcode, errmsg, headers)
  701.         
  702.         stuff = headers['www-authenticate']
  703.         import re
  704.         match = re.match('[ \t]*([^ \t]+)[ \t]+realm="([^"]*)"', stuff)
  705.         if not match:
  706.             URLopener.http_error_default(self, url, fp, errcode, errmsg, headers)
  707.         
  708.         (scheme, realm) = match.groups()
  709.         if scheme.lower() != 'basic':
  710.             URLopener.http_error_default(self, url, fp, errcode, errmsg, headers)
  711.         
  712.         name = 'retry_' + self.type + '_basic_auth'
  713.         if data is None:
  714.             return getattr(self, name)(url, realm)
  715.         else:
  716.             return getattr(self, name)(url, realm, data)
  717.  
  718.     
  719.     def retry_http_basic_auth(self, url, realm, data = None):
  720.         (host, selector) = splithost(url)
  721.         i = host.find('@') + 1
  722.         host = host[i:]
  723.         (user, passwd) = self.get_user_passwd(host, realm, i)
  724.         if not user:
  725.             pass
  726.         if not passwd:
  727.             return None
  728.         
  729.         host = quote(user, safe = '') + ':' + quote(passwd, safe = '') + '@' + host
  730.         newurl = 'http://' + host + selector
  731.         if data is None:
  732.             return self.open(newurl)
  733.         else:
  734.             return self.open(newurl, data)
  735.  
  736.     
  737.     def retry_https_basic_auth(self, url, realm, data = None):
  738.         (host, selector) = splithost(url)
  739.         i = host.find('@') + 1
  740.         host = host[i:]
  741.         (user, passwd) = self.get_user_passwd(host, realm, i)
  742.         if not user:
  743.             pass
  744.         if not passwd:
  745.             return None
  746.         
  747.         host = quote(user, safe = '') + ':' + quote(passwd, safe = '') + '@' + host
  748.         newurl = '//' + host + selector
  749.         return self.open_https(newurl, data)
  750.  
  751.     
  752.     def get_user_passwd(self, host, realm, clear_cache = 0):
  753.         key = realm + '@' + host.lower()
  754.         if key in self.auth_cache:
  755.             if clear_cache:
  756.                 del self.auth_cache[key]
  757.             else:
  758.                 return self.auth_cache[key]
  759.         
  760.         (user, passwd) = self.prompt_user_passwd(host, realm)
  761.         if user or passwd:
  762.             self.auth_cache[key] = (user, passwd)
  763.         
  764.         return (user, passwd)
  765.  
  766.     
  767.     def prompt_user_passwd(self, host, realm):
  768.         '''Override this in a GUI environment!'''
  769.         import getpass
  770.         
  771.         try:
  772.             user = raw_input('Enter username for %s at %s: ' % (realm, host))
  773.             passwd = getpass.getpass('Enter password for %s in %s at %s: ' % (user, realm, host))
  774.             return (user, passwd)
  775.         except KeyboardInterrupt:
  776.             print 
  777.             return (None, None)
  778.  
  779.  
  780.  
  781. _localhost = None
  782.  
  783. def localhost():
  784.     """Return the IP address of the magic hostname 'localhost'."""
  785.     global _localhost
  786.     if _localhost is None:
  787.         _localhost = socket.gethostbyname('localhost')
  788.     
  789.     return _localhost
  790.  
  791. _thishost = None
  792.  
  793. def thishost():
  794.     '''Return the IP address of the current host.'''
  795.     global _thishost
  796.     if _thishost is None:
  797.         _thishost = socket.gethostbyname(socket.gethostname())
  798.     
  799.     return _thishost
  800.  
  801. _ftperrors = None
  802.  
  803. def ftperrors():
  804.     '''Return the set of errors raised by the FTP class.'''
  805.     global _ftperrors
  806.     if _ftperrors is None:
  807.         import ftplib
  808.         _ftperrors = ftplib.all_errors
  809.     
  810.     return _ftperrors
  811.  
  812. _noheaders = None
  813.  
  814. def noheaders():
  815.     '''Return an empty mimetools.Message object.'''
  816.     global _noheaders
  817.     if _noheaders is None:
  818.         import mimetools
  819.         import StringIO
  820.         _noheaders = mimetools.Message(StringIO.StringIO(), 0)
  821.         _noheaders.fp.close()
  822.     
  823.     return _noheaders
  824.  
  825.  
  826. class ftpwrapper:
  827.     '''Class used by open_ftp() for cache of open FTP connections.'''
  828.     
  829.     def __init__(self, user, passwd, host, port, dirs):
  830.         self.user = user
  831.         self.passwd = passwd
  832.         self.host = host
  833.         self.port = port
  834.         self.dirs = dirs
  835.         self.init()
  836.  
  837.     
  838.     def init(self):
  839.         import ftplib
  840.         self.busy = 0
  841.         self.ftp = ftplib.FTP()
  842.         self.ftp.connect(self.host, self.port)
  843.         self.ftp.login(self.user, self.passwd)
  844.         for dir in self.dirs:
  845.             self.ftp.cwd(dir)
  846.         
  847.  
  848.     
  849.     def retrfile(self, file, type):
  850.         import ftplib
  851.         self.endtransfer()
  852.         if type in ('d', 'D'):
  853.             cmd = 'TYPE A'
  854.             isdir = 1
  855.         else:
  856.             cmd = 'TYPE ' + type
  857.             isdir = 0
  858.         
  859.         try:
  860.             self.ftp.voidcmd(cmd)
  861.         except ftplib.all_errors:
  862.             self.init()
  863.             self.ftp.voidcmd(cmd)
  864.  
  865.         conn = None
  866.         if file and not isdir:
  867.             
  868.             try:
  869.                 self.ftp.nlst(file)
  870.             except ftplib.error_perm:
  871.                 reason = None
  872.                 raise IOError, ('ftp error', reason), sys.exc_info()[2]
  873.  
  874.             self.ftp.voidcmd(cmd)
  875.             
  876.             try:
  877.                 cmd = 'RETR ' + file
  878.                 conn = self.ftp.ntransfercmd(cmd)
  879.             except ftplib.error_perm:
  880.                 reason = None
  881.                 if str(reason)[:3] != '550':
  882.                     raise IOError, ('ftp error', reason), sys.exc_info()[2]
  883.                 
  884.             except:
  885.                 str(reason)[:3] != '550'
  886.             
  887.  
  888.         None<EXCEPTION MATCH>ftplib.error_perm
  889.         if not conn:
  890.             self.ftp.voidcmd('TYPE A')
  891.             if file:
  892.                 cmd = 'LIST ' + file
  893.             else:
  894.                 cmd = 'LIST'
  895.             conn = self.ftp.ntransfercmd(cmd)
  896.         
  897.         self.busy = 1
  898.         return (addclosehook(conn[0].makefile('rb'), self.endtransfer), conn[1])
  899.  
  900.     
  901.     def endtransfer(self):
  902.         if not (self.busy):
  903.             return None
  904.         
  905.         self.busy = 0
  906.         
  907.         try:
  908.             self.ftp.voidresp()
  909.         except ftperrors():
  910.             pass
  911.  
  912.  
  913.     
  914.     def close(self):
  915.         self.endtransfer()
  916.         
  917.         try:
  918.             self.ftp.close()
  919.         except ftperrors():
  920.             pass
  921.  
  922.  
  923.  
  924.  
  925. class addbase:
  926.     '''Base class for addinfo and addclosehook.'''
  927.     
  928.     def __init__(self, fp):
  929.         self.fp = fp
  930.         self.read = self.fp.read
  931.         self.readline = self.fp.readline
  932.         if hasattr(self.fp, 'readlines'):
  933.             self.readlines = self.fp.readlines
  934.         
  935.         if hasattr(self.fp, 'fileno'):
  936.             self.fileno = self.fp.fileno
  937.         
  938.         if hasattr(self.fp, '__iter__'):
  939.             self.__iter__ = self.fp.__iter__
  940.             if hasattr(self.fp, 'next'):
  941.                 self.next = self.fp.next
  942.             
  943.         
  944.  
  945.     
  946.     def __repr__(self):
  947.         return '<%s at %s whose fp = %s>' % (self.__class__.__name__, `id(self)`, `self.fp`)
  948.  
  949.     
  950.     def close(self):
  951.         self.read = None
  952.         self.readline = None
  953.         self.readlines = None
  954.         self.fileno = None
  955.         if self.fp:
  956.             self.fp.close()
  957.         
  958.         self.fp = None
  959.  
  960.  
  961.  
  962. class addclosehook(addbase):
  963.     '''Class to add a close hook to an open file.'''
  964.     
  965.     def __init__(self, fp, closehook, *hookargs):
  966.         addbase.__init__(self, fp)
  967.         self.closehook = closehook
  968.         self.hookargs = hookargs
  969.  
  970.     
  971.     def close(self):
  972.         addbase.close(self)
  973.         if self.closehook:
  974.             self.closehook(*self.hookargs)
  975.             self.closehook = None
  976.             self.hookargs = None
  977.         
  978.  
  979.  
  980.  
  981. class addinfo(addbase):
  982.     '''class to add an info() method to an open file.'''
  983.     
  984.     def __init__(self, fp, headers):
  985.         addbase.__init__(self, fp)
  986.         self.headers = headers
  987.  
  988.     
  989.     def info(self):
  990.         return self.headers
  991.  
  992.  
  993.  
  994. class addinfourl(addbase):
  995.     '''class to add info() and geturl() methods to an open file.'''
  996.     
  997.     def __init__(self, fp, headers, url):
  998.         addbase.__init__(self, fp)
  999.         self.headers = headers
  1000.         self.url = url
  1001.  
  1002.     
  1003.     def info(self):
  1004.         return self.headers
  1005.  
  1006.     
  1007.     def geturl(self):
  1008.         return self.url
  1009.  
  1010.  
  1011.  
  1012. def basejoin(base, url):
  1013.     '''Utility to combine a URL with a base URL to form a new URL.'''
  1014.     (type, path) = splittype(url)
  1015.     if type:
  1016.         return url
  1017.     
  1018.     (host, path) = splithost(path)
  1019.     (type, basepath) = splittype(base)
  1020.     if host:
  1021.         if type:
  1022.             return type + '://' + host + path
  1023.         else:
  1024.             return url
  1025.     
  1026.     (host, basepath) = splithost(basepath)
  1027.     (basepath, basetag) = splittag(basepath)
  1028.     (basepath, basequery) = splitquery(basepath)
  1029.     if path[:1] != '/':
  1030.         if path[:1] in ('#', '?'):
  1031.             i = len(basepath)
  1032.         else:
  1033.             i = basepath.rfind('/')
  1034.         if i < 0:
  1035.             if host:
  1036.                 basepath = '/'
  1037.             else:
  1038.                 basepath = ''
  1039.         else:
  1040.             basepath = basepath[:i + 1]
  1041.         while basepath and path[:3] == '../':
  1042.             path = path[3:]
  1043.             i = basepath[:-1].rfind('/')
  1044.             if i > 0:
  1045.                 basepath = basepath[:i + 1]
  1046.                 continue
  1047.             if i == 0:
  1048.                 basepath = '/'
  1049.                 break
  1050.                 continue
  1051.             basepath = ''
  1052.         path = basepath + path
  1053.     
  1054.     if host and path and path[0] != '/':
  1055.         path = '/' + path
  1056.     
  1057.     if type and host:
  1058.         return type + '://' + host + path
  1059.     elif type:
  1060.         return type + ':' + path
  1061.     elif host:
  1062.         return '//' + host + path
  1063.     else:
  1064.         return path
  1065.  
  1066.  
  1067. try:
  1068.     unicode
  1069. except NameError:
  1070.     
  1071.     def _is_unicode(x):
  1072.         return 0
  1073.  
  1074.  
  1075.  
  1076. def _is_unicode(x):
  1077.     return isinstance(x, unicode)
  1078.  
  1079.  
  1080. def toBytes(url):
  1081.     '''toBytes(u"URL") --> \'URL\'.'''
  1082.     if _is_unicode(url):
  1083.         
  1084.         try:
  1085.             url = url.encode('ASCII')
  1086.         except UnicodeError:
  1087.             raise UnicodeError('URL ' + repr(url) + ' contains non-ASCII characters')
  1088.         except:
  1089.             None<EXCEPTION MATCH>UnicodeError
  1090.         
  1091.  
  1092.     None<EXCEPTION MATCH>UnicodeError
  1093.     return url
  1094.  
  1095.  
  1096. def unwrap(url):
  1097.     """unwrap('<URL:type://host/path>') --> 'type://host/path'."""
  1098.     url = url.strip()
  1099.     if url[:1] == '<' and url[-1:] == '>':
  1100.         url = url[1:-1].strip()
  1101.     
  1102.     if url[:4] == 'URL:':
  1103.         url = url[4:].strip()
  1104.     
  1105.     return url
  1106.  
  1107. _typeprog = None
  1108.  
  1109. def splittype(url):
  1110.     """splittype('type:opaquestring') --> 'type', 'opaquestring'."""
  1111.     global _typeprog
  1112.     if _typeprog is None:
  1113.         import re
  1114.         _typeprog = re.compile('^([^/:]+):')
  1115.     
  1116.     match = _typeprog.match(url)
  1117.     if match:
  1118.         scheme = match.group(1)
  1119.         return (scheme.lower(), url[len(scheme) + 1:])
  1120.     
  1121.     return (None, url)
  1122.  
  1123. _hostprog = None
  1124.  
  1125. def splithost(url):
  1126.     """splithost('//host[:port]/path') --> 'host[:port]', '/path'."""
  1127.     global _hostprog
  1128.     if _hostprog is None:
  1129.         import re
  1130.         _hostprog = re.compile('^//([^/]*)(.*)$')
  1131.     
  1132.     match = _hostprog.match(url)
  1133.     if match:
  1134.         return match.group(1, 2)
  1135.     
  1136.     return (None, url)
  1137.  
  1138. _userprog = None
  1139.  
  1140. def splituser(host):
  1141.     """splituser('user[:passwd]@host[:port]') --> 'user[:passwd]', 'host[:port]'."""
  1142.     global _userprog
  1143.     if _userprog is None:
  1144.         import re
  1145.         _userprog = re.compile('^(.*)@(.*)$')
  1146.     
  1147.     match = _userprog.match(host)
  1148.     if match:
  1149.         return map(unquote, match.group(1, 2))
  1150.     
  1151.     return (None, host)
  1152.  
  1153. _passwdprog = None
  1154.  
  1155. def splitpasswd(user):
  1156.     """splitpasswd('user:passwd') -> 'user', 'passwd'."""
  1157.     global _passwdprog
  1158.     if _passwdprog is None:
  1159.         import re
  1160.         _passwdprog = re.compile('^([^:]*):(.*)$')
  1161.     
  1162.     match = _passwdprog.match(user)
  1163.     if match:
  1164.         return match.group(1, 2)
  1165.     
  1166.     return (user, None)
  1167.  
  1168. _portprog = None
  1169.  
  1170. def splitport(host):
  1171.     """splitport('host:port') --> 'host', 'port'."""
  1172.     global _portprog
  1173.     if _portprog is None:
  1174.         import re
  1175.         _portprog = re.compile('^(.*):([0-9]+)$')
  1176.     
  1177.     match = _portprog.match(host)
  1178.     if match:
  1179.         return match.group(1, 2)
  1180.     
  1181.     return (host, None)
  1182.  
  1183. _nportprog = None
  1184.  
  1185. def splitnport(host, defport = -1):
  1186.     """Split host and port, returning numeric port.
  1187.     Return given default port if no ':' found; defaults to -1.
  1188.     Return numerical port if a valid number are found after ':'.
  1189.     Return None if ':' but not a valid number."""
  1190.     global _nportprog
  1191.     if _nportprog is None:
  1192.         import re
  1193.         _nportprog = re.compile('^(.*):(.*)$')
  1194.     
  1195.     match = _nportprog.match(host)
  1196.     if match:
  1197.         (host, port) = match.group(1, 2)
  1198.         
  1199.         try:
  1200.             if not port:
  1201.                 raise ValueError, 'no digits'
  1202.             
  1203.             nport = int(port)
  1204.         except ValueError:
  1205.             nport = None
  1206.  
  1207.         return (host, nport)
  1208.     
  1209.     return (host, defport)
  1210.  
  1211. _queryprog = None
  1212.  
  1213. def splitquery(url):
  1214.     """splitquery('/path?query') --> '/path', 'query'."""
  1215.     global _queryprog
  1216.     if _queryprog is None:
  1217.         import re
  1218.         _queryprog = re.compile('^(.*)\\?([^?]*)$')
  1219.     
  1220.     match = _queryprog.match(url)
  1221.     if match:
  1222.         return match.group(1, 2)
  1223.     
  1224.     return (url, None)
  1225.  
  1226. _tagprog = None
  1227.  
  1228. def splittag(url):
  1229.     """splittag('/path#tag') --> '/path', 'tag'."""
  1230.     global _tagprog
  1231.     if _tagprog is None:
  1232.         import re
  1233.         _tagprog = re.compile('^(.*)#([^#]*)$')
  1234.     
  1235.     match = _tagprog.match(url)
  1236.     if match:
  1237.         return match.group(1, 2)
  1238.     
  1239.     return (url, None)
  1240.  
  1241.  
  1242. def splitattr(url):
  1243.     """splitattr('/path;attr1=value1;attr2=value2;...') ->
  1244.         '/path', ['attr1=value1', 'attr2=value2', ...]."""
  1245.     words = url.split(';')
  1246.     return (words[0], words[1:])
  1247.  
  1248. _valueprog = None
  1249.  
  1250. def splitvalue(attr):
  1251.     """splitvalue('attr=value') --> 'attr', 'value'."""
  1252.     global _valueprog
  1253.     if _valueprog is None:
  1254.         import re
  1255.         _valueprog = re.compile('^([^=]*)=(.*)$')
  1256.     
  1257.     match = _valueprog.match(attr)
  1258.     if match:
  1259.         return match.group(1, 2)
  1260.     
  1261.     return (attr, None)
  1262.  
  1263.  
  1264. def splitgophertype(selector):
  1265.     """splitgophertype('/Xselector') --> 'X', 'selector'."""
  1266.     if selector[:1] == '/' and selector[1:2]:
  1267.         return (selector[1], selector[2:])
  1268.     
  1269.     return (None, selector)
  1270.  
  1271.  
  1272. def unquote(s):
  1273.     """unquote('abc%20def') -> 'abc def'."""
  1274.     mychr = chr
  1275.     myatoi = int
  1276.     list = s.split('%')
  1277.     res = [
  1278.         list[0]]
  1279.     myappend = res.append
  1280.     del list[0]
  1281.     for item in list:
  1282.         if item[1:2]:
  1283.             
  1284.             try:
  1285.                 myappend(mychr(myatoi(item[:2], 16)) + item[2:])
  1286.             except ValueError:
  1287.                 myappend('%' + item)
  1288.             except:
  1289.                 None<EXCEPTION MATCH>ValueError
  1290.             
  1291.  
  1292.         None<EXCEPTION MATCH>ValueError
  1293.         myappend('%' + item)
  1294.     
  1295.     return ''.join(res)
  1296.  
  1297.  
  1298. def unquote_plus(s):
  1299.     """unquote('%7e/abc+def') -> '~/abc def'"""
  1300.     if '+' in s:
  1301.         s = ' '.join(s.split('+'))
  1302.     
  1303.     return unquote(s)
  1304.  
  1305. always_safe = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_.-'
  1306. _fast_safe_test = always_safe + '/'
  1307. _fast_safe = None
  1308.  
  1309. def _fast_quote(s):
  1310.     global _fast_safe
  1311.     if _fast_safe is None:
  1312.         _fast_safe = { }
  1313.         for c in _fast_safe_test:
  1314.             _fast_safe[c] = c
  1315.         
  1316.     
  1317.     res = list(s)
  1318.     for i in range(len(res)):
  1319.         c = res[i]
  1320.         if not (c in _fast_safe):
  1321.             res[i] = '%%%02X' % ord(c)
  1322.             continue
  1323.     
  1324.     return ''.join(res)
  1325.  
  1326.  
  1327. def quote(s, safe = '/'):
  1328.     '''quote(\'abc def\') -> \'abc%20def\'
  1329.  
  1330.     Each part of a URL, e.g. the path info, the query, etc., has a
  1331.     different set of reserved characters that must be quoted.
  1332.  
  1333.     RFC 2396 Uniform Resource Identifiers (URI): Generic Syntax lists
  1334.     the following reserved characters.
  1335.  
  1336.     reserved    = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" |
  1337.                   "$" | ","
  1338.  
  1339.     Each of these characters is reserved in some component of a URL,
  1340.     but not necessarily in all of them.
  1341.  
  1342.     By default, the quote function is intended for quoting the path
  1343.     section of a URL.  Thus, it will not encode \'/\'.  This character
  1344.     is reserved, but in typical usage the quote function is being
  1345.     called on a path where the existing slash characters are used as
  1346.     reserved characters.
  1347.     '''
  1348.     safe = always_safe + safe
  1349.     if _fast_safe_test == safe:
  1350.         return _fast_quote(s)
  1351.     
  1352.     res = list(s)
  1353.     for i in range(len(res)):
  1354.         c = res[i]
  1355.         if c not in safe:
  1356.             res[i] = '%%%02X' % ord(c)
  1357.             continue
  1358.     
  1359.     return ''.join(res)
  1360.  
  1361.  
  1362. def quote_plus(s, safe = ''):
  1363.     """Quote the query fragment of a URL; replacing ' ' with '+'"""
  1364.     if ' ' in s:
  1365.         l = s.split(' ')
  1366.         for i in range(len(l)):
  1367.             l[i] = quote(l[i], safe)
  1368.         
  1369.         return '+'.join(l)
  1370.     else:
  1371.         return quote(s, safe)
  1372.  
  1373.  
  1374. def urlencode(query, doseq = 0):
  1375.     '''Encode a sequence of two-element tuples or dictionary into a URL query string.
  1376.  
  1377.     If any values in the query arg are sequences and doseq is true, each
  1378.     sequence element is converted to a separate parameter.
  1379.  
  1380.     If the query arg is a sequence of two-element tuples, the order of the
  1381.     parameters in the output will match the order of parameters in the
  1382.     input.
  1383.     '''
  1384.     if hasattr(query, 'items'):
  1385.         query = query.items()
  1386.     else:
  1387.         
  1388.         try:
  1389.             if len(query) and not isinstance(query[0], tuple):
  1390.                 raise TypeError
  1391.         except TypeError:
  1392.             (ty, va, tb) = sys.exc_info()
  1393.             raise TypeError, 'not a valid non-string sequence or mapping object', tb
  1394.  
  1395.     l = []
  1396.     if not doseq:
  1397.         for k, v in query:
  1398.             k = quote_plus(str(k))
  1399.             v = quote_plus(str(v))
  1400.             l.append(k + '=' + v)
  1401.         
  1402.     else:
  1403.         for k, v in query:
  1404.             k = quote_plus(str(k))
  1405.             if isinstance(v, str):
  1406.                 v = quote_plus(v)
  1407.                 l.append(k + '=' + v)
  1408.                 continue
  1409.             if _is_unicode(v):
  1410.                 v = quote_plus(v.encode('ASCII', 'replace'))
  1411.                 l.append(k + '=' + v)
  1412.                 continue
  1413.             
  1414.             try:
  1415.                 x = len(v)
  1416.             except TypeError:
  1417.                 v = quote_plus(str(v))
  1418.                 l.append(k + '=' + v)
  1419.                 continue
  1420.  
  1421.             for elt in v:
  1422.                 l.append(k + '=' + quote_plus(str(elt)))
  1423.             
  1424.         
  1425.     return '&'.join(l)
  1426.  
  1427.  
  1428. def getproxies_environment():
  1429.     '''Return a dictionary of scheme -> proxy server URL mappings.
  1430.  
  1431.     Scan the environment for variables named <scheme>_proxy;
  1432.     this seems to be the standard convention.  If you need a
  1433.     different way, you can pass a proxies dictionary to the
  1434.     [Fancy]URLopener constructor.
  1435.  
  1436.     '''
  1437.     proxies = { }
  1438.     for name, value in os.environ.items():
  1439.         name = name.lower()
  1440.         if value and name[-6:] == '_proxy':
  1441.             proxies[name[:-6]] = value
  1442.             continue
  1443.     
  1444.     return proxies
  1445.  
  1446. if os.name == 'mac':
  1447.     
  1448.     def getproxies():
  1449.         '''Return a dictionary of scheme -> proxy server URL mappings.
  1450.  
  1451.         By convention the mac uses Internet Config to store
  1452.         proxies.  An HTTP proxy, for instance, is stored under
  1453.         the HttpProxy key.
  1454.  
  1455.         '''
  1456.         
  1457.         try:
  1458.             import ic
  1459.         except ImportError:
  1460.             return { }
  1461.  
  1462.         
  1463.         try:
  1464.             config = ic.IC()
  1465.         except ic.error:
  1466.             return { }
  1467.  
  1468.         proxies = { }
  1469.         if 'UseHTTPProxy' in config and config['UseHTTPProxy']:
  1470.             
  1471.             try:
  1472.                 value = config['HTTPProxyHost']
  1473.             except ic.error:
  1474.                 pass
  1475.  
  1476.             proxies['http'] = 'http://%s' % value
  1477.         
  1478.         return proxies
  1479.  
  1480.     
  1481.     def proxy_bypass(x):
  1482.         return 0
  1483.  
  1484. elif os.name == 'nt':
  1485.     
  1486.     def getproxies_registry():
  1487.         '''Return a dictionary of scheme -> proxy server URL mappings.
  1488.  
  1489.         Win32 uses the registry to store proxies.
  1490.  
  1491.         '''
  1492.         proxies = { }
  1493.         
  1494.         try:
  1495.             import _winreg
  1496.         except ImportError:
  1497.             return proxies
  1498.  
  1499.         
  1500.         try:
  1501.             internetSettings = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, 'Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings')
  1502.             proxyEnable = _winreg.QueryValueEx(internetSettings, 'ProxyEnable')[0]
  1503.             if proxyEnable:
  1504.                 proxyServer = str(_winreg.QueryValueEx(internetSettings, 'ProxyServer')[0])
  1505.                 if '=' in proxyServer:
  1506.                     for p in proxyServer.split(';'):
  1507.                         (protocol, address) = p.split('=', 1)
  1508.                         import re
  1509.                         if not re.match('^([^/:]+)://', address):
  1510.                             address = '%s://%s' % (protocol, address)
  1511.                         
  1512.                         proxies[protocol] = address
  1513.                     
  1514.                 elif proxyServer[:5] == 'http:':
  1515.                     proxies['http'] = proxyServer
  1516.                 else:
  1517.                     proxies['http'] = 'http://%s' % proxyServer
  1518.                     proxies['ftp'] = 'ftp://%s' % proxyServer
  1519.             
  1520.             internetSettings.Close()
  1521.         except (WindowsError, ValueError, TypeError):
  1522.             pass
  1523.  
  1524.         return proxies
  1525.  
  1526.     
  1527.     def getproxies():
  1528.         '''Return a dictionary of scheme -> proxy server URL mappings.
  1529.  
  1530.         Returns settings gathered from the environment, if specified,
  1531.         or the registry.
  1532.  
  1533.         '''
  1534.         if not getproxies_environment():
  1535.             pass
  1536.         return getproxies_registry()
  1537.  
  1538.     
  1539.     def proxy_bypass(host):
  1540.         
  1541.         try:
  1542.             import _winreg
  1543.             import re
  1544.         except ImportError:
  1545.             return 0
  1546.  
  1547.         
  1548.         try:
  1549.             internetSettings = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, 'Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings')
  1550.             proxyEnable = _winreg.QueryValueEx(internetSettings, 'ProxyEnable')[0]
  1551.             proxyOverride = str(_winreg.QueryValueEx(internetSettings, 'ProxyOverride')[0])
  1552.         except WindowsError:
  1553.             return 0
  1554.  
  1555.         if not proxyEnable or not proxyOverride:
  1556.             return 0
  1557.         
  1558.         host = [
  1559.             host]
  1560.         
  1561.         try:
  1562.             addr = socket.gethostbyname(host[0])
  1563.             if addr != host:
  1564.                 host.append(addr)
  1565.         except socket.error:
  1566.             pass
  1567.  
  1568.         proxyOverride = proxyOverride.split(';')
  1569.         i = 0
  1570.         while i < len(proxyOverride):
  1571.             if proxyOverride[i] == '<local>':
  1572.                 proxyOverride[i:i + 1] = [
  1573.                     'localhost',
  1574.                     '127.0.0.1',
  1575.                     socket.gethostname(),
  1576.                     socket.gethostbyname(socket.gethostname())]
  1577.             
  1578.             i += 1
  1579.         for test in proxyOverride:
  1580.             test = test.replace('.', '\\.')
  1581.             test = test.replace('*', '.*')
  1582.             test = test.replace('?', '.')
  1583.             for val in host:
  1584.                 if re.match(test, val, re.I):
  1585.                     return 1
  1586.                     continue
  1587.             
  1588.         
  1589.         return 0
  1590.  
  1591. else:
  1592.     getproxies = getproxies_environment
  1593.     
  1594.     def proxy_bypass(host):
  1595.         return 0
  1596.  
  1597.  
  1598. def test1():
  1599.     s = ''
  1600.     for i in range(256):
  1601.         s = s + chr(i)
  1602.     
  1603.     s = s * 4
  1604.     t0 = time.time()
  1605.     qs = quote(s)
  1606.     uqs = unquote(qs)
  1607.     t1 = time.time()
  1608.     if uqs != s:
  1609.         print 'Wrong!'
  1610.     
  1611.     print `s`
  1612.     print `qs`
  1613.     print `uqs`
  1614.     print round(t1 - t0, 3), 'sec'
  1615.  
  1616.  
  1617. def reporthook(blocknum, blocksize, totalsize):
  1618.     print 'Block number: %d, Block size: %d, Total size: %d' % (blocknum, blocksize, totalsize)
  1619.  
  1620.  
  1621. def test(args = []):
  1622.     if not args:
  1623.         args = [
  1624.             '/etc/passwd',
  1625.             'file:/etc/passwd',
  1626.             'file://localhost/etc/passwd',
  1627.             'ftp://ftp.python.org/pub/python/README',
  1628.             'http://www.python.org/index.html']
  1629.         if hasattr(URLopener, 'open_https'):
  1630.             args.append('https://synergy.as.cmu.edu/~geek/')
  1631.         
  1632.     
  1633.     
  1634.     try:
  1635.         for url in args:
  1636.             print '-' * 10, url, '-' * 10
  1637.             (fn, h) = urlretrieve(url, None, reporthook)
  1638.             print fn
  1639.             if h:
  1640.                 print '======'
  1641.                 for k in h.keys():
  1642.                     print k + ':', h[k]
  1643.                 
  1644.                 print '======'
  1645.             
  1646.             fp = open(fn, 'rb')
  1647.             data = fp.read()
  1648.             del fp
  1649.             if '\r' in data:
  1650.                 table = string.maketrans('', '')
  1651.                 data = data.translate(table, '\r')
  1652.             
  1653.             print data
  1654.             (fn, h) = (None, None)
  1655.         
  1656.         print '-' * 40
  1657.     finally:
  1658.         urlcleanup()
  1659.  
  1660.  
  1661.  
  1662. def main():
  1663.     import getopt
  1664.     import sys
  1665.     
  1666.     try:
  1667.         (opts, args) = getopt.getopt(sys.argv[1:], 'th')
  1668.     except getopt.error:
  1669.         msg = None
  1670.         print msg
  1671.         print 'Use -h for help'
  1672.         return None
  1673.  
  1674.     t = 0
  1675.     for o, a in opts:
  1676.         if o == '-t':
  1677.             t = t + 1
  1678.         
  1679.         if o == '-h':
  1680.             print 'Usage: python urllib.py [-t] [url ...]'
  1681.             print '-t runs self-test;', 'otherwise, contents of urls are printed'
  1682.             return None
  1683.             continue
  1684.     
  1685.     if t:
  1686.         if t > 1:
  1687.             test1()
  1688.         
  1689.         test(args)
  1690.     elif not args:
  1691.         print 'Use -h for help'
  1692.     
  1693.     for url in args:
  1694.         print urlopen(url).read(),
  1695.     
  1696.  
  1697. if __name__ == '__main__':
  1698.     main()
  1699.  
  1700.